﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Sockets;
/// <summary>
/// Summary description for MessageQueue
/// </summary>
public class MessageQueue
{
    private static readonly int MEM_BLOCK = 256;
    private static readonly int MEM_PREPARE = 128;
    private static readonly int MEM_FLAT = 24;

    private int m_nBufSize;
    private int m_nDatSize;
    private int m_nDatOffs;
    private byte[] m_Buf;

    public MessageQueue()
    {
        m_nBufSize = OptimizeBufferSize(0);
        m_nDatSize = 0;
        m_nDatOffs = 0;

        m_Buf = new byte[m_nBufSize];
    }

    public MessageQueue(int nInitSize)
    {
        m_nBufSize = OptimizeBufferSize(nInitSize);
        m_nDatSize = 0;
        m_nDatOffs = 0;

        m_Buf = new byte[m_nBufSize];
    }

    private int OptimizeBufferSize(int nSize)
    {
        return (nSize / MEM_BLOCK + 1) * MEM_BLOCK;
    }

    private bool PrepareBuffer(int nNextDatSize)
    {
        //New data size for next using
        int nNewDatSize = m_nDatSize + nNextDatSize;

        //Check if there's not enough buffer for new next data from the m_nDatOffs
        if (m_nDatOffs + nNewDatSize > m_nBufSize)
        {
            //Check if it must reallocate buffer
            if (nNewDatSize > m_nBufSize)
            {
                //Calculate new data size for optimization
                int nNewBufSize = OptimizeBufferSize(nNewDatSize);
                //Allocate new buffer
                byte[] tmpBuf = new byte[nNewBufSize];

                if (tmpBuf == null)
                    return false;

                //Copy data to new buffer then update all related info
                System.Array.Copy(m_Buf, m_nDatOffs, tmpBuf, 0, m_nDatSize);

                //m_nDatSize no change
                m_nBufSize = nNewBufSize;
                m_nDatOffs = 0;
                m_Buf = tmpBuf;
            }
            else //It's enough buffer for next data if the current data move to the beginning of buffer
            {
                //Copy data to new buffer then update all related info
                System.Array.Copy(m_Buf, m_nDatOffs, m_Buf, 0, m_nDatSize);

                //m_nDatSize no change
                //m_nBufSize no change
                m_nDatOffs = 0;
                //m_Buf no change
            }
        }

        return true;
    }

    public bool Push(Socket sock)
    {
        try
        {
            if (m_nBufSize < (m_nDatOffs + m_nDatSize + MEM_FLAT))
            {
                if (!PrepareBuffer(MEM_PREPARE))
                    return false;
            }

            int nRecv = sock.Receive(m_Buf, m_nDatOffs + m_nDatSize, m_nBufSize - m_nDatOffs - m_nDatSize, SocketFlags.None);

            if (nRecv > 0)
            {
                //m_LogFile.Write(m_Buf, m_nDatOffs + m_nDatSize, nRecv);
                m_nDatSize += nRecv;
                return true;
            }
        }
        catch
        {
        }

        return false;
    }

    public bool Push(byte[] buff, int nSize)
    {
        if (PrepareBuffer(nSize))
        {
            //Copy data 
            System.Array.Copy(buff, 0, m_Buf, m_nDatOffs + m_nDatSize, nSize);
            m_nDatSize += nSize;
            return true;
        }

        return false;
    }

    public bool Push(byte[] buff, int nOffset, int nSize)
    {
        if (PrepareBuffer(nSize))
        {
            //Copy data 
            System.Array.Copy(buff, nOffset, m_Buf, m_nDatOffs + m_nDatSize, nSize);
            m_nDatSize += nSize;
            return true;
        }

        return false;
    }

    public bool PopWebMessage(out byte[] msg, out int nMsgOffs, out int nMsgLen)
    {
        try
        {
            while (m_nDatSize >= Message.EMPTY_MESSAGE_LENGTH)
            {
                if ((m_Buf[m_nDatOffs] != '#') || (m_Buf[m_nDatOffs + 1] != '#'))
                {
                    m_nDatOffs++;
                    m_nDatSize--;
                }
                else
                {
                    nMsgLen = BitConverter.ToInt32(m_Buf, m_nDatOffs + 2);

                    if (nMsgLen > m_nDatSize)
                        break;

                    if ((m_Buf[m_nDatOffs + nMsgLen - 2] != '\r') ||
                        (m_Buf[m_nDatOffs + nMsgLen - 1] != '\n'))
                    {
                        m_nDatOffs++;
                        m_nDatSize--;
                        continue;
                    }

                    nMsgOffs = m_nDatOffs;
                    //nMsgLen no change
                    msg = m_Buf;

                    //Update info
                    //m_nBufSize no change
                    //m_Buf no change
                    m_nDatOffs += nMsgLen;
                    m_nDatSize -= nMsgLen;

                    return true;
                }
            }
        }
        catch
        {
        }

        msg = null;
        nMsgOffs = nMsgLen = 0;
        return false;
    }
}

